{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "C++中栈和堆上建立对象的区别\n",
    "https://www.cnblogs.com/xiaoxiaoqiang001/p/5557704.html\n",
    "在C++中类的对象建立分为两种，一种是静态建立，如A a；另一种是动态建立，如A* p=new A()，A*p=(A*)malloc()；静态建立一个类对象，是由编译器为对象在栈空间中分配内存，通过直接移动栈顶指针挪出适当的空间，然后在这片内存空间上调用构造函数形成一个栈对象。动态建立类对象，是使用new运算符将对象建立在堆空间中，在栈中只保留了指向该对象的指针。栈是由编译器自动分配释放 ，存放函数的参数值，局部变量的值，对象的引用地址等。其操作方式类似于数据结构中的栈，通常都是被调用时处于存储空间中，调用完毕立即释放。堆中通常保存程序运行时动态创建的对象，C++堆中存放的对象需要由程序员分配释放，它存在程序运行的整个生命期，直到程序结束由OS释放。而在java中通常类的对象都分配在堆中，对象的回收由虚拟机的GC垃圾回收机制决定。\n",
    "\n",
    "1.下面的程序来看看静态建立和动态建立对象的区别\n",
    "\n",
    "复制代码\n",
    " 1 #include<iostream>\n",
    " 2 #include<string>\n",
    " 3 using namespace std;\n",
    " 4 class  student\n",
    " 5 {\n",
    " 6 public:\n",
    " 7     string name;\n",
    " 8     int age;\n",
    " 9     void sayhello();\n",
    "10 };\n",
    "11 void student::sayhello()\n",
    "12 {\n",
    "13     cout<<\"my name is: \"+this->name+\" I am: \"<<this->age;\n",
    "14     cout<<\"\\n\";\n",
    "15 }\n",
    "16 student setname(string name)\n",
    "17 {\n",
    "18     student stu;\n",
    "19     stu.age=12;\n",
    "20     stu.name=name;\n",
    "21     return stu;\n",
    "22 }\n",
    "23 int main()\n",
    "24 {\n",
    "25     student stu=setname(\"jim\");\n",
    "26     stu.sayhello();\n",
    "27     return 0;\n",
    "28 }\n",
    "复制代码\n",
    "程序运行结果：my name is: jim I am: 12;\n",
    "\n",
    "程序定义了一个student类，在setname函数中定义一个局部对象作为返回值。程序第18行静态构建了一个student对象stu，它在栈上分配空间，在函数调用结束后就销毁了，函数返回的类对应在内存中的值应该不存在啊？其实原来C++在用类作为函数的返回值时调用了类的拷贝构造函数，而且该拷贝构造函数是在堆上分配存储空间，后面再讨论这个问题。\n",
    "\n",
    "在setname函数内的stu在函数调用结束后就销毁了，可以添加一个析构函数来证明：\n",
    "\n",
    "在student类中加入析构函数：\n",
    "\n",
    "student::~student()\n",
    "{\n",
    "    cout<<this->name<<\":gameover\"<<endl;\n",
    "}\n",
    "程序运行结果：\n",
    "\n",
    "\n",
    "\n",
    "在sayhello()前，输出jim:gameover，即为setname()里的stu对象执行了析构函数。\n",
    "\n",
    "如将setname函数改为：\n",
    "\n",
    "复制代码\n",
    "student* setname(string name)\n",
    "{\n",
    "    student stu;\n",
    "    stu.age=12;\n",
    "    stu.name=name;\n",
    "    return &stu;\n",
    "}\n",
    "复制代码\n",
    "main函数的调用改为：\n",
    "\n",
    "int main()\n",
    "{\n",
    "    student* p=setname(\"tom\");\n",
    "    p->sayhello();\n",
    "    return 0;\n",
    "}\n",
    "显然这里会出现问题，对象指针返回的是栈上的对象，在函数调用结束后已经销毁了，对象指针即为野指针，故程序在编译时会提示：warning C4172: returning address of local variable or temporary。解决这个问题我们自然想到把该对象构建在堆上即可。修改setname函数为下：\n",
    "\n",
    "复制代码\n",
    " student* setname(string name)\n",
    " {\n",
    "student* stu= new student();\n",
    "stu->age=12;\n",
    "stu->name=name;\n",
    "return  stu;\n",
    " }\n",
    "复制代码\n",
    "main函数的调用不变；程序正常运行输出：\n",
    "\n",
    "\n",
    "上面输出结果并没有调用析构函数，在setname调用后，在main函数结束后也没有调用。在对上的对象需要程序员自己delete释放，将main改为如下：\n",
    "\n",
    "复制代码\n",
    "int main()\n",
    "{\n",
    "    student* p=setname(\"tom\");\n",
    "    p->sayhello();\n",
    "    delete p;\n",
    "    return 0;\n",
    "}\n",
    "复制代码\n",
    "即加入delete p;运行结果：\n",
    "\n",
    "\n",
    "C中用malloc函数来动态申请空间，该内存分配在堆上。这里可以验证，加入#include <malloc.h>，将setname函数改为如下：\n",
    "\n",
    "复制代码\n",
    "1  student* setname(string name)\n",
    "2  {\n",
    "3 student* stu=(student*)malloc(sizeof(student));\n",
    "4 stu->age=12;\n",
    "5 stu->name=name;\n",
    "6 return  stu;\n",
    "7  }\n",
    "复制代码\n",
    "为在student中加入构造函数：\n",
    "\n",
    "student::student()\n",
    "{\n",
    "    cout<<\"constructor\"<<endl;\n",
    "}\n",
    "上面的程序执行到第5行会出错，原因是没有调用构造函数，stu->name根本就没有被初始化（string的构造函数没有被调用），所以不能赋值 。具体的解释是： 因为malloc只是分配堆内存（不会调用构造函数）它并不知道内存里要存的是什么。为此用new即可，将第3行代码改为：student* stu=new student;程序运行结果为下：\n",
    "\n",
    "\n",
    "\n",
    "即程序调用了构造函数。若非要用malloc来申请内存可以将setname函数改为如下：\n",
    "\n",
    "复制代码\n",
    "1  student* setname(string name)\n",
    "2  {\n",
    "3 student* stu=(student*)malloc(sizeof(student));\n",
    "4 new(stu) student;\n",
    "5 stu->age=12;\n",
    "6 stu->name=name;\n",
    "7 return  stu;\n",
    "8  }\n",
    "复制代码\n",
    "即加入了第4行程序正常运行，调用了构造函数。第4行大概可以理解为new了一个student对象，赋值转换为student的指针stu。\n",
    "既然这样可以把第3行直接改为：student* stu；即\n",
    "\n",
    "复制代码\n",
    "1  student* setname(string name)\n",
    "2  {\n",
    "3 //student*stu= new student;\n",
    "4 student* stu;\n",
    "5 new(stu) student;\n",
    "6 stu->age=12;\n",
    "7 stu->name=name;\n",
    "8 return  stu;\n",
    "9  }\n",
    "复制代码\n",
    "让说第4,5行的效果应该和第3行相同，编译程序提示：warning C4700: local variable 'stu' used without having been initialized，即stu没有初始化。 new(stu) student;和stu= new student;并不等价。第5行并不是初始化，这里可以看做第5行这种写法（之前还真没见过）是C++为兼容malloc内存申请的用法，一般情况下推荐肯定是用new关键字。\n",
    "到此其实这里要说明的主题已经基本说明了，关于malloc的用法当申请的类的成员变量只包含基本的数据类型（数值型int，double等）（string等引用类除外）时是不会出错的。下面的列子可以证明；\n",
    "\n",
    "复制代码\n",
    " 1 #include<iostream>\n",
    " 2 #include<string>\n",
    " 3 #include <malloc.h>\n",
    " 4 using namespace std;\n",
    " 5 class course\n",
    " 6 {\n",
    " 7 public:\n",
    " 8     int id;\n",
    " 9     float score;\n",
    "10     void printscore()\n",
    "11     {\n",
    "12     cout<<\"id:\"<<this->id;\n",
    "13         cout<<\" score:\"<<this->score<<endl;\n",
    "14     }\n",
    "15 };\n",
    "16 \n",
    "17  course* setscore(int id,float score)\n",
    "18  {\n",
    "19      course* co= (course*)malloc(sizeof(course));\n",
    "20      co->id=id;\n",
    "21      co->score=score;\n",
    "22      return co;\n",
    "23  }\n",
    "24 int main()\n",
    "25 {\n",
    "26 course* cou=setscore(999,188.9);\n",
    "27 cou->printscore();\n",
    "28     return 0;\n",
    "29 }\n",
    "复制代码\n",
    "程序运行结果如下：\n",
    "\n",
    "\n",
    "\n",
    " 程序第19行这样的用法没有问题，而之前的string类却有问题。这样看来，自定义类型作为类的成员时也应该会有问题，来看下面的代码：\n",
    "\n",
    "复制代码\n",
    " 1 #include<iostream>\n",
    " 2 #include<string>\n",
    " 3 #include <malloc.h>\n",
    " 4 using namespace std;\n",
    " 5 class course;\n",
    " 6 class  student\n",
    " 7 {\n",
    " 8 public:\n",
    " 9     string  name;\n",
    "10     int age;\n",
    "11         course cou;\n",
    "12     void sayhello();\n",
    "13     ~student();\n",
    "14     student();\n",
    "15 };\n",
    "16 student::student()\n",
    "17 {\n",
    "18     cout<<\"constructor\"<<endl;\n",
    "19 }\n",
    "20 student::~student()\n",
    "21 {\n",
    "22     cout<<this->name<<\":gameover\"<<endl;\n",
    "23 }\n",
    "24 void student::sayhello()\n",
    "25 {\n",
    "26     cout<<\"my name is: \"+this->name+\" I am: \"<<this->age;\n",
    "27     cout<<\"\\n\";\n",
    "28 }\n",
    "29 class course\n",
    "30 {\n",
    "31 public:\n",
    "32     int id;\n",
    "33     float score;\n",
    "34     void printscore()\n",
    "35     {\n",
    "36     cout<<\"id:\"<<this->id;\n",
    "37         cout<<\" score:\"<<this->score<<endl;\n",
    "38     }\n",
    "39 };\n",
    "40  course* setscore(int id,float score)\n",
    "41  {\n",
    "42      course* co= (course*)malloc(sizeof(course));\n",
    "43      co->id=id;\n",
    "44      co->score=score;\n",
    "45      return co;\n",
    "46  }\n",
    "47  student* setname_score(string name ,course* cou)\n",
    "48  {\n",
    "49 student* stu= (student*)malloc(sizeof(student));\n",
    "50 stu->age=12;\n",
    "51 new(stu)student;\n",
    "52 stu->cou.id=cou->id;\n",
    "53 stu->cou.score=cou->score;\n",
    "54 stu->name= name;\n",
    "55 return  stu;\n",
    "56  }\n",
    "57 int main()\n",
    "58 {\n",
    "59 course* cou=setscore(999,188.9);\n",
    "60 student* stu=setname_score(\"jimm\",cou);\n",
    "61 stu->cou.printscore();\n",
    "62 stu->sayhello();\n",
    "63 return 0;\n",
    "64 }\n",
    "复制代码\n",
    "这段代码中把course类对象作为student的类成员。程序编译出错： error C2079: 'cou' uses undefined class 'course'。把course类的定义放在前面则没有错即：\n",
    "\n",
    " View Code\n",
    "运行结果为：\n",
    "\n",
    "\n",
    "\n",
    "上面程序setname_score函数中若不用new(stu)student;这种写法，则会出现未初始化的错误。这里完全可以将类的成员改为指针的形式，在初始化时用new在堆上分配存储。改写的代码如下：\n",
    "\n",
    " View Code\n",
    "上面程序运行结果为：\n",
    "\n",
    "\n",
    "综上所述，C++中对象的建立可以在堆和栈上。分别为动态建立和动态建立的方式，构建堆上的对象时一般使用new关键字，而对象的指针在栈上。使用new在堆上构建的对象需要主动的delete销毁。C++对象可以在堆或栈中，函数的传参可以是对象(对象的拷贝)，或是对象的指针。而在java中对象一般分配在堆上，对象的传值只有值类型，即对象的引用（地址），这样看来C++要灵活的多。关于c++数组的内存分配还有这里提到的拷贝构造函数，下次再讨论啊。上面的程序在VC++6.0编写通过。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.3"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": false
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
